package game;

import java.net.*;
import java.util.*;
import java.io.*;

/*
 * class communicating with all players through BadugiClient, with game logic
 */
public class BadugiServer extends Thread implements Runnable {
	private Random r;
	private ServerSocket ssocket;
	private BadugiClient[] clients;
	private Deck deck;
	private int maxbet;
	private int[] tokens;
	private int[] lastexchange;
	private boolean[] exchange;
	private String[] lastaction;
	public static int port=4444;
	private int smallblind,bigblind;
	private boolean bet;
	private int endturnready;
	/*
	 * which player's turn is now
	 */
	private int playerturn;
	/*
	 * stages
	 * 0  game didn't started yet
	 * 1  dealer button
	 * 2  small blind, big blind
	 * 3  giving cards to players
	 * 4  1st licitation
	 * 5  card exchange
	 * 6  2nd licitation
	 * 7  card exchange
	 * 8  3rd licitation
	 * 9  card exchange
	 * 10 4th licitation
	 */
	private int stage;
	private int dbutton;
	
	BadugiServer() {
		tokens=new int[6];
		lastexchange=new int[6];
		exchange=new boolean[6];
		clients=new BadugiClient[6];
		lastaction=new String[6];
		maxbet=0;
		endturnready=0;
		r=new Random();
		deck=new Deck();
		playerturn=0;
		stage=0;
		dbutton=r.nextInt(6);
		deck.Shuffle();
		for (int i=0;i<6;i++) {
			exchange[i]=false;
			tokens[i]=0;
			clients[i]=new BadugiClient(this,i);
			clients[i].start();
			lastexchange[i]=0;
			lastaction[i]="";
		}
		try {
			ssocket=new ServerSocket(port);
		} catch (IOException e) {
			System.out.println("Can't create ssocket");
		}
	}

	@Override
	public void run() {
		int i=0;
		while(true) {
			try {
				if (clients[i].slotready) {
					clients[i].socket=ssocket.accept();
					clients[i].writer=new PrintWriter(clients[i].socket.getOutputStream(), true);
					clients[i].reader=new BufferedReader(new InputStreamReader(clients[i].socket.getInputStream()));
					if (stage==0) {
						clients[i].setPlayerName(clients[i].reader.readLine());
						clients[i].slotready=false;
						tokens[i]=10000;
						sendinfo();
					} else {
						clients[i].writer.println("MGame has started on this server");
					}
				}
			} catch (IOException e) {
			}
			i++;
			i=i%6;
		}
	}
	
	public String gamelogic(int ID,String msg) {
		/*
		 * prefixes
		 * M[msg] - message
		 * B- player folded
		 * C- player checked
		 * D[int] - player betted
		 * E[com] - admin command
		 * F[int] - player raised
		 * G- player Called
		 * H- All In
		 * IXXXX- exchanging cards where X is Y and not where its N (eg. IYYNY exhanges cards 1,2 and 4)
		 * J[ID][int]- number of tokens
		 * K[int][int2]- u got card int2 on place int
		 * L[ID][place][string]-set player [ID] info [place] to string
		 * S[int]- stage int
		 * Y- your move
		 * Q[ID]- player [ID] has quit
		 */
		
		if (msg.equals("Q")) {
			clients[ID].setName("");
			clients[ID].player.bet=0;
			clients[ID].slotready=true;
			lastaction[ID]="";
			lastexchange[ID]=0;
			for (int i=0;i<4;i++) {
				if (clients[ID].player.cards[i]!=52) {
				try {
					deck.InsertCard(clients[ID].player.cards[i]);
					clients[ID].player.cards[i]=52;
				} catch (BadugiException e) {
				}
				}
			}
			sendinfo();
			return "Q";
		}
		if (msg.equals("Ebot1")) {
			int bot=6;
			for(int i=0;i<6;i++) {
				if (clients[i].slotready && clients[i].player.bot==0) {
					bot=i;
					i=6;
				}
			}
			if (bot==6) {
				return "Mno free spaces";
			}
			clients[bot].player.bot=1;
			clients[bot].setPlayerName("bot "+bot);
			tokens[bot]=10000;
			clients[bot].player.bet=0;
			lastaction[bot]="";
			lastexchange[bot]=0;
			sendinfo();
		}
		
		if(stage==0) {
			if (msg.length()>6 && msg.substring(0,6).equals("Estart")) {
				int bigblind=Integer.parseInt(msg.substring(6));
				boolean rightblinds=false;
				maxbet=0;
				smallblind=6;
				this.bigblind=6;
				for (int i=0;i<6;i++) {
					if (tokens[(dbutton+1+i)%6]>=bigblind/2 && maxbet!=bigblind/2) {
						maxbet=bigblind/2;
						smallblind=(dbutton+1+i)%6;
					} else if (tokens[(dbutton+1+i)%6]>=bigblind) {
						maxbet=bigblind;
						this.bigblind=(dbutton+1+i)%6;
						rightblinds=true;
						i=6;
					}
				}
				if (bigblind<2 || bigblind>10000 || !(rightblinds)) {
					return "MWrong blinds";
				}
					clients[smallblind].player.bet=bigblind/2;
					if (!(clients[smallblind].slotready)) {
						clients[smallblind].writer.println("Msmall blind = "+bigblind/2);
					}
					clients[this.bigblind].player.bet=bigblind;
					if (!(clients[this.bigblind].slotready)) {
						clients[this.bigblind].writer.println("Mbig blind = "+bigblind);
					}
				for (int i=0;i<6;i++) {
					if (!(clients[i].slotready)) {
						clients[i].writer.println("MGame has started");
					}
				}
				for (int i=0;i<6;i++) {
					if (!(clients[i].slotready && clients[i].player.bot==0))
					{
						clients[i].player.isinplay=true;
						for (int j=0;j<4;j++) {
							try {
								clients[i].player.cards[j]=deck.TakeCard();
							} catch (BadugiException e) {
							}
							if (!(clients[i].slotready)) {
								clients[i].writer.println("K"+j+clients[i].player.cards[j]);
							}
						}
					}
				}
				stage=4;
				bet=true;
				endturnready=-1;
				checkplayer((this.bigblind+1)%6);
				sendinfo();
				return "MGame has started";
			}
			return "M Waiting for game start";
		} else if (stage==4 || stage==6 || stage==8 || stage==10) {
			if (ID==playerturn) {//this player's turn
				boolean rightmove=false;
				if (msg.equals("B")) {//fold
					clients[ID].player.isinplay=false;
					lastaction[ID]="FOLD";
					rightmove=true;
				} else if (msg.equals("G") && bet) {//call
					if (tokens[ID]<=maxbet) {
						return "MYou can't call";
					}
					lastaction[ID]="CALL";
					rightmove=true;
					clients[ID].player.bet=maxbet;
				} else if (msg.substring(0,1).equals("F") && bet) {//raise
					int raise;
					try {
						raise=Integer.parseInt(msg.substring(1));
					} catch (NumberFormatException e) {
						return "MWrong data";
					}
					if (raise<=0) {
						return "MWrong number";
					}
					if (clients[ID].player.bet+raise>tokens[ID]) {
						return "MYou don't have so much tokens";
					}
					lastaction[ID]="RAISE";
					rightmove=true;
					maxbet=maxbet+raise;
					clients[ID].player.bet=maxbet;
				} else if (msg.substring(0,1).equals("D") && !(bet)) {//bet
					bet=true;
					int newbet;
					try {
						newbet=Integer.parseInt(msg.substring(1));
					} catch (NumberFormatException e) {
						return "MWrong data";
					}
					if (newbet<=0) {
						return "MWrong number";
					}
					if (maxbet+newbet>tokens[ID]) {
						return "MYou don't have so much tokens";
					}
					lastaction[ID]="BET";
					rightmove=true;
					maxbet=maxbet+newbet;
					clients[ID].player.bet=maxbet;
				} else if (msg.equals("C")) {//check
					if (clients[ID].player.bet==maxbet) {
						lastaction[ID]="CHECK";
						rightmove=true;
					} else return "MYou can't check";
				} else if (msg.equals("H")) {//all in
					lastaction[ID]="ALL IN";
					rightmove=true;
					clients[ID].player.allin=true;
					if (maxbet<tokens[ID]) maxbet=tokens[ID];
					clients[ID].player.bet=tokens[ID];
				}
				
				boolean endturn=true;
				for (int i=0;i<6;i++) {
					if (!(clients[i].slotready && clients[i].player.bot==0) && clients[i].player.isinplay) {
						if (clients[i].player.bet!=maxbet && !(clients[i].player.allin)) {
							endturn=false;
						}
					}
				}
				if (endturn && endturnready>4) {
					if (stage==10) {//game end
						stage=0;
						int bestplayer=0;
						boolean tie=false;
						int[] beststats=new int[2];
						beststats[0]=0;
						beststats[1]=13;
						for (int i=0;i<6;i++) {//searching best player
							if (!(clients[i].slotready) && clients[i].player.isinplay)
							{
								if (beststats[0]==0) {
									beststats=clients[i].player.CheckCards();
									bestplayer=i;
								} else {
									int[] newstats=clients[i].player.CheckCards();
									if (beststats[0]<newstats[0] || (newstats[0]==beststats[0] && newstats[1]<beststats[1])) {
										beststats[0]=newstats[0];
										beststats[1]=newstats[1];
										bestplayer=i;
										tie=false;
									} else if (beststats[0]==newstats[0] && beststats[1]==newstats[1]) {
										tie=true;
									}
								} 
							}
						}
						if (tie) {//tie
							for (int i=0;i<6;i++) {
								tokens[i]-=clients[i].player.bet;
								clients[i].writer.println("MYou lost " + clients[i].player.bet + " tokens");
								clients[i].player.bet=0;
								lastexchange[i]=0;
								lastaction[i]="";
							}
						} else {//someone wins
							for (int i=0;i<6;i++) {
								if (i!=bestplayer) {
									tokens[i]-=clients[i].player.bet;
									tokens[bestplayer]+=clients[i].player.bet;
								}
								clients[i].player.bet=0;
								lastexchange[i]=0;
								lastaction[i]="";
							}
						}
						for (int i=0;i<6;i++) {
							for (int j=0;j<4;j++) {
								if (clients[i].player.cards[j]!=52) {
								try {
									deck.InsertCard(clients[i].player.cards[j]);
									clients[i].player.cards[j]=52;
								} catch (BadugiException e) {
								}
								}
							}
						}
						deck.Shuffle();
						sendinfo();
						dbutton++;
						return "Mgame end, thank you for playing";
					} else {//licitation end
						stage++;
						sendinfo();
						for (int i=0;i<6;i++) {
							if (!(clients[i].slotready)) {
								if (clients[i].player.isinplay) {
									exchange[i]=true;
									clients[i].writer.println("MExchange cards");
								}
							}
						}
						System.out.println("exchange starts");
						return "MExchange cards";
					}
				} else if (rightmove) {
					sendinfo();
					checkplayer((ID+1)%6);
					return "";
				}
			} else return "MIt's not your turn";
		} else if ((stage==5 || stage==7 || stage==9) && msg.substring(0,1).equals("I")) {//exchange stage
			if (exchange[ID]) {
				int exchangedcards=0;
				exchange[ID]=false;
				for (int i=0;i<4;i++) {
					if (msg.substring(i+1,i+2).equals("Y")) {
						exchangedcards++;
						clients[ID].player.cards[i]=deck.ExchangeCard(clients[ID].player.cards[i]);
						clients[ID].writer.println("K"+i+clients[ID].player.cards[i]);
					}
				}
				lastexchange[ID]=exchangedcards;
				boolean exrdy=true;
				for (int i=0;i<6;i++) {
					if (exchange[i]) exrdy=false;
				}
				if (exrdy) {
					stage++;
					bet=false;
					endturnready=0;
					checkplayer(smallblind);
				}
				sendinfo();
				return "Mexchanged " + exchangedcards + " cards";
			}
		}
		return "Mwrong move";
	}
	
	private void checkplayer(int ID) {
		playerturn=ID;
		endturnready++;
		if (clients[ID].player.bot>0 && clients[ID].slotready) {
			AImove(ID);
		} else if (!(clients[ID].slotready)) {
			clients[ID].writer.println("Y");
		} else {
			checkplayer((ID+1)%6);
		}
	}
	
	private void AImove(int ID) {
		gamelogic(ID,"H");
		checkplayer((ID+1)%6);
	}
	
	private void sendinfo() {
		for (int i=0;i<6;i++) {
			if (!(clients[i].slotready) || clients[i].player.bot>0) {
				for (int j=0;j<6;j++) {
					if (!(clients[j].slotready)) {
						clients[j].writer.println("L"+(j-i+5)%6+0+clients[i].getPlayerName());
						clients[j].writer.println("L"+(j-i+5)%6+1+(tokens[i]-clients[i].player.bet));
						clients[j].writer.println("L"+(j-i+5)%6+2+"bet "+clients[i].player.bet);
						clients[j].writer.println("L"+(j-i+5)%6+3+lastexchange[i]);
						clients[j].writer.println("L"+(j-i+5)%6+4+lastaction[i]);
					}
				}
			}
		}
	}
}
